import React, { useCallback, useContext, useRef, useState } from 'react';
import {
  Icon,
  Tooltip,
  Input,
  Select,
  Checkbox,
  Menu,
  Dropdown,
  message,
  Drawer,
  Popconfirm,
  Row,
  Typography,
} from 'antd';
import PropTypes from 'prop-types';
import get from 'lodash/get';
import classnames from 'classnames';
import ValidatorContext from '../ValidatorContext';
import {
  SCHEMA_TYPE,
  STRING_TYPE,
  ARRAY_TYPE,
  OBJECT_TYPE,
  BOOLEAN_TYPE,
  RULES,
  NUMBER_TYPE,
} from '../settings';
import { hasChildren } from '@/utils/utils';
import ForString from './ForString';
import styles from '../styles.less';
import { CONDITION_TRUE } from '@/pages/Transformer/settings';
import { nodeName } from '@/utils/node';

const { Option } = Select;
const { Text } = Typography;

const timestamp = +new Date();

// 预期props包含nodePath, parent, current, dispatch, close
const mapping = props => {
  const { current } = props;

  const type =
    typeof get(current, 'nodeType') === 'string' && get(current, 'nodeType').toLowerCase();

  switch (type) {
    case 'string':
      return <ForString {...props} />;

    case 'number':
      // return <ForNumber {...props} />;
      return (
        <ForString
          filter={({ value }) =>
            [RULES.required, RULES.length, RULES.range, RULES.enums, RULES.regex, RULES.custom]
              .map(item => item.type)
              .includes(value)
          }
          {...props}
        />
      );
    case 'array':
      return (
        <ForString
          filter={({ value }) =>
            [RULES.required, RULES.not_empty, RULES.length, RULES.custom]
              .map(item => item.type)
              .includes(value)
          }
          {...props}
        />
      );

    case 'object':
      return (
        <ForString
          filter={({ value }) =>
            [RULES.required, RULES.not_empty, RULES.custom].map(item => item.type).includes(value)
          }
          {...props}
        />
      );

    case 'boolean':
      return (
        <ForString
          filter={({ value }) => [RULES.required].map(item => item.type).includes(value)}
          {...props}
        />
      );

    default:
      return null;
  }
};

// 时间戳大的 优先级高，表示是当前操作的模式（全局or本地 toggle）
function isShow(global, local) {
  const { timestamp: lt, status: ls } = local;

  if (typeof get(global, 'timestamp') !== 'number') {
    return ls;
  }
  const { timestamp: gt, status: gs } = global;

  return gt > lt ? gs : ls;
}

function Node({ parent, nodePath, current, offset, children }) {
  const { state = {}, dispatch } = useContext(ValidatorContext);

  const isRoot = nodePath === '';

  const parentNodeType = get(parent, 'nodeType');

  const isArrayItem = parentNodeType === ARRAY_TYPE;

  const [width, setWidth] = useState(500);

  const [settingVisible, setSettingVisible] = useState(false);

  // 根节点默认展开
  const [localToggle, setLocalToggle] = useState(() => ({
    timestamp,
    status: isRoot,
  }));

  const prefix = isRoot ? '' : `${nodePath}.`;

  // 适用于除`rules`外的节点普通属性，
  const changeNodeNormalAttr = useCallback(
    (value, path, attrName) => {
      dispatch({
        type: 'update_node_normal_attr',
        payload: {
          value,
          path,
          current,
          attrName,
        },
      });
    },
    [dispatch, parent]
  );

  const addSibingNode = useCallback(
    (parent, current, nodePath) => {
      dispatch({
        type: 'add_sibling_node',
        payload: {
          parent,
          current,
          nodePath,
        },
      });
    },
    [dispatch]
  );

  const addChildNode = useCallback(
    (parent, current, nodePath) => {
      dispatch({
        type: 'add_child_node',
        payload: {
          parent,
          current,
          nodePath,
        },
      });
    },
    [dispatch]
  );

  const deleteCurrentNode = useCallback(
    nodePath => {
      dispatch({
        type: 'delete_current_node',
        payload: {
          nodePath,
        },
      });
    },
    [dispatch]
  );

  const copyCurrentNode = useCallback(
    (parent, current, nodePath) => {
      dispatch({
        type: 'copy_current_node',
        payload: {
          parent,
          current,
          nodePath,
        },
      });
    },
    [dispatch]
  );

  const isRulesBasic = rules => {
    return (
      rules === undefined ||
      rules.length === 0 ||
      (rules.length === 1 && get(rules, '[0].condition.type') === CONDITION_TRUE)
    );
  };

  const isRequired = node => {
    const rules = get(node, 'rules') || [];
    if (!isRulesBasic(rules) || rules.length === 0) {
      return false;
    }
    const { config } = rules[0];
    return Array.isArray(config) && config.some(k => k.type === 'required' && k.required !== false);
  };

  const isNotEmpty = node => {
    const rules = get(node, 'rules') || [];
    if (!isRulesBasic(rules) || rules.length === 0) {
      return false;
    }
    const { config } = rules[0];
    return (
      Array.isArray(config) && config.some(k => k.type === 'not_empty' && k.notEmpty !== false)
    );
  };

  // 根节点的parent预期是undefined
  function renderPlus(parent, current, nodePath) {
    const { nodeType } = current;

    const parentNodeType = get(parent, 'nodeType');

    const isRoot = parent === undefined;

    const isObjectRoot = isRoot && nodeType === OBJECT_TYPE;

    // ## 只添加子节点
    // 1、根节点是object
    // 2、当前是object且父节点是array
    // 3、当前是array但还没有children
    const onlyChild =
      isObjectRoot ||
      (parentNodeType === ARRAY_TYPE && nodeType === OBJECT_TYPE) ||
      (nodeType === ARRAY_TYPE && !hasChildren(current));

    // ## 只有兄弟节点
    // 1、父节点不是array的普通节点(指除除object、boolean和array外)
    // 2、父节点是object且自身不是object的
    const onlySibling =
      (SCHEMA_TYPE.filter(k => k !== ARRAY_TYPE).includes(parentNodeType) &&
        ![OBJECT_TYPE, ARRAY_TYPE].includes(nodeType)) ||
      (parentNodeType === OBJECT_TYPE && nodeType !== OBJECT_TYPE);

    //
    const childAndSibling = nodeType === OBJECT_TYPE && !isRoot;

    if (onlyChild) {
      return (
        <Tooltip placement="top" title="子节点">
          <Icon
            type="plus"
            className={classnames(styles.action, styles.add)}
            onClick={() => {
              addChildNode(parent, current, nodePath);

              // 保证子节点添加完可见，避免添加时父节点是`收起`状态
              if (localToggle.status === false) {
                setLocalToggle({
                  timestamp: +new Date(),
                  status: true,
                });
              }
            }}
          />
        </Tooltip>
      );
    }

    if (onlySibling) {
      return (
        <Tooltip placement="top" title="兄弟节点">
          <Icon
            type="plus"
            className={classnames(styles.action, styles.add)}
            onClick={() => addSibingNode(parent, current, nodePath)}
          />
        </Tooltip>
      );
    }

    // 父节点是array时, 子节点
    if (childAndSibling) {
      return (
        <Dropdown
          overlay={
            <Menu>
              <Menu.Item>
                <span onClick={() => addSibingNode(parent, current, nodePath)}>兄弟节点</span>
              </Menu.Item>
              <Menu.Item>
                <span
                  onClick={() => {
                    addChildNode(parent, current, nodePath);

                    // 保证子节点添加完可见，避免添加时父节点是`收起`状态
                    if (localToggle.status === false) {
                      setLocalToggle({
                        timestamp: +new Date(),
                        status: true,
                      });
                    }
                  }}
                >
                  子节点
                </span>
              </Menu.Item>
            </Menu>
          }
        >
          <Icon type="plus" className={classnames(styles.action, styles.add)} />
        </Dropdown>
      );
    }

    return null;
  }

  // open是内部状态
  const show = isShow(state.globalToggle, localToggle);

  const inputRef = useRef(null);

  const { rules } = current;

  const isBasic = isRulesBasic(rules);

  return (
    <Row
      type="flex"
      align="middle"
      onDoubleClick={e => {
        e.stopPropagation();
      }}
    >
      {/* 防止Drawer双击 冒泡到 <Expandable />组件 */}
      {/* 展开/收起 */}
      {/* {hasChildren(current) && (
          <i
            className={classnames(styles['wtree-switcher'])}
            onClick={() =>
              setLocalToggle({
                timestamp: +new Date(),
                status: !show,
              })
            }
          >
            {show === true ? '-' : '+'}
          </i>
        )} */}

      {/* 属性名称 */}

      <i
        className={classnames(
          'tgIcon',
          `icon-${current.nodeType}`.toLocaleLowerCase(),
          styles.icon,
          styles[`${current.nodeType}`.toLocaleLowerCase()]
        )}
      />

      <Input
        ref={inputRef}
        onClick={() => inputRef.current.select()}
        defaultValue={get(current, 'nodeName')}
        onBlur={e => {
          if (e.target.value === current.nodeName) {
            return;
          }

          if (!e.target.value) {
            message.error('key不能为空');
          } else if (
            Array.isArray(parent.children) &&
            parent.children.some(k => k.nodeName === e.target.value)
          ) {
            message.error('当前key重复了');
          } else {
            dispatch({
              type: 'modify_node_name',
              payload: {
                node: current,
                nodePath,
                newName: e.target.value,
              },
            });
          }
        }}
        className={styles.nodeName}
        disabled={isRoot || isArrayItem}
        style={{
          width: width - offset,
        }}
      />

      {/* 是否必填 */}
      <Tooltip placement="top" title={isRoot ? '全选' : '必填'}>
        <Checkbox
          disabled={!isBasic}
          onChange={e => {
            dispatch({
              type: 'toggle_required',
              payload: {
                checked: e.target.checked,
                nodePath,
              },
            });
          }}
          checked={isRequired(current)}
        />
      </Tooltip>

      {/* 是否非空 */}
      <Tooltip placement="top" title={isRoot ? '全选' : '非空'}>
        <Checkbox
          onChange={e => {
            dispatch({
              type: 'toggle_not_empty',
              payload: {
                checked: e.target.checked,
                nodePath,
              },
            });
          }}
          disabled={
            isRoot ||
            !isBasic ||
            current.nodeType === BOOLEAN_TYPE ||
            current.nodeType === NUMBER_TYPE
          }
          checked={isNotEmpty(current)}
          style={{ marginLeft: 0 }}
        />
      </Tooltip>

      {/* 值类型 */}
      <Select
        value={get(current, `nodeType`) || STRING_TYPE}
        placeholder="未知"
        onChange={value => {
          changeNodeNormalAttr(value, `${prefix}nodeType`, 'nodeType');
          // 因为array和object时，会自动最近一个默认子节点
          if ([OBJECT_TYPE, ARRAY_TYPE].includes(value)) {
            setLocalToggle({
              timestamp: +new Date(),
              status: true,
            });
          }
        }}
      >
        {SCHEMA_TYPE.map(item => {
          return (
            <Option value={item} key={item}>
              {item}
            </Option>
          );
        })}
      </Select>

      {/* 操作 */}
      <Tooltip placement="top" title="高级设置">
        <Icon
          type="setting"
          className={classnames(styles.action, styles.setting)}
          onClick={() => setSettingVisible(true)}
        />
      </Tooltip>

      {/* 根节点和数组的子节点 不需要删除 */}
      {parentNodeType !== ARRAY_TYPE && (
        <Tooltip placement="top" title="删除">
          {hasChildren(current) ? (
            <Popconfirm
              title="注意，所有子节点都会被删除！"
              onConfirm={() => deleteCurrentNode(nodePath)}
            >
              <Icon type="delete" className={classnames(styles.action, styles.delete)} />
            </Popconfirm>
          ) : (
            <Icon
              type="delete"
              className={classnames(styles.action, styles.delete)}
              onClick={() => deleteCurrentNode(nodePath)}
            />
          )}
        </Tooltip>
      )}

      {renderPlus(parent, current, nodePath)}

      {/* {parentNodeType !== ARRAY_TYPE && !isRoot && (
        <Tooltip placement="top" title="复制节点">
          <Icon
            type="copy"
            className={classnames(styles.action, styles.copy)}
            onClick={() => copyCurrentNode(parent, current, nodePath)}
          />
        </Tooltip>
      )} */}

      {show && children}
      {settingVisible && (
        <Drawer
          title={
            <>
              高级设置{' '}
              <Text code>
                {current.nodeName}({nodeName(current.nodeType)})
              </Text>
            </>
          }
          width={700}
          onClose={() => setSettingVisible(false)}
          visible={settingVisible}
          bodyStyle={{ paddingBottom: 80 }}
          destroyOnClose
        >
          {mapping({ nodePath, parent, current, dispatch, close: setSettingVisible })}
        </Drawer>
      )}
    </Row>
  );
}

const NODE_TYPE = {
  nodeTitle: PropTypes.string,
  nodeName: PropTypes.string,
  nodeType: PropTypes.oneOf(SCHEMA_TYPE),
  children: PropTypes.array,
};

Node.propTypes = {
  parent: PropTypes.shape(NODE_TYPE),
  nodePath: PropTypes.string.isRequired,
  current: PropTypes.shape(NODE_TYPE).isRequired,
};

Node.defaultProps = {
  parent: undefined,
};

export default Node;
